package logic

import (
	"context"
	"strings"

	"mall/common/modelx"
	"mall/common/mqueuex"
	"mall/common/resultx"
	"mall/service/order/model"
	"mall/service/order/rpc/internal/svc"
	"mall/service/order/rpc/types/order"

	"github.com/zeromicro/go-zero/core/logx"
)

type ListLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ListLogic {
	return &ListLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

func (l *ListLogic) List(in *order.ListRequest) (*order.ListResponse, error) {
	logx.Infof("ListRequest: %+v ", in)
	// ListRequest: 

	if in.Preload == "" && in.EagerLoad == "" {
		return nil, resultx.StatusError(resultx.REUQEST_PARAM_ERROR, "")
	}

	preloadMap, eagerLoadMap := modelx.GetPreloadMap(in.Preload, in.EagerLoad)
	logx.Infof("preloadMap: %+v ", preloadMap)
	// preloadMap:
	logx.Infof("eagerLoadMap: %+v ", eagerLoadMap)
	// eagerLoadMap:

	orderEntityName := model.RpcEntityNames.Order
	_, hasEntitySettings := model.RpcEntityPreloadMap[orderEntityName] // Order的关联字段
	if !hasEntitySettings {
		return &order.ListResponse{}, nil
	}

	orderRepositoryName := model.RepositoryNames.Order
	columnMap, hasColumnMap := model.RepositoryPreloadMap[orderRepositoryName] // Order的数据库字段
	if !hasColumnMap {
		return &order.ListResponse{}, nil
	}

	var selectCols []string
	if preloadFields, hasPreloadFields := preloadMap[orderEntityName]; hasPreloadFields {
		selectColMap := make(map[string]string)
		for _, field := range preloadFields {
			if _, exist := selectColMap[field]; !exist {
				if column, hasColumn := columnMap[field]; hasColumn {
					selectColMap[field] = column
				} else {
					selectColMap[field] = ""
				}
			}
		}
		for _, column := range selectColMap {
			if column != "" {
				selectCols = append(selectCols, column)
			}
		}
	}

	var count int64
	var err error
	var orderList []*model.Order
	var total int64
	var preTotal int64 = -1
	var filterMap map[string]map[string][]interface{}
	if in.Filter != "" {
		filterMap = modelx.GetFilterMap(in.Filter, columnMap)
		logx.Infof("filterMap: %+v ", filterMap)
		// filterMap:
	}
	if filterMap == nil {
		filterMap = make(map[string]map[string][]interface{})
	}
	filterMap[model.OrderColumns.TradeState] = map[string][]interface{}{
		"moreThan": []interface{}{mqueuex.OrderTradeStateUserDeleted},
	}
	if in.PreFilterEntity != "" && in.PreFilter != "" {
		orderShipmentRpcEntityName := model.RpcEntityNames.OrderShipment
		orderShipmentRepositoryName := model.RepositoryNames.OrderShipment
		if orderShipmentRpcEntityName == in.PreFilterEntity {
			_, hasRpcEntitySettingMap := model.RpcEntityPreloadMap[orderShipmentRpcEntityName]
			preColumnMap, hasPreColumnMap := model.RepositoryPreloadMap[orderShipmentRepositoryName]
			if hasRpcEntitySettingMap && hasPreColumnMap {
				var oids []interface{} // []model.Order.ID
				preFilterMap := modelx.GetFilterMap(in.PreFilter, preColumnMap)
				logx.Infof("preFilterMap: %+v ", preFilterMap)
				// preFilterMap:
				preOrderBy := modelx.GetOrderBy(in.PreSorter, preColumnMap)
				logx.Infof("preOrderBy: %s ", preOrderBy)
				// preOrderBy:
				_, preTotal, err = l.svcCtx.Order.GetShipmentList(l.ctx, model.M{}, 0, preFilterMap)
				if err != nil {
					return nil, err
				}

				preList, preCount, err := l.svcCtx.Order.GetShipmentList(l.ctx, model.M{
					"offset":  in.Offset,
					"limit":   in.Limit,
					"orderBy": preOrderBy,
					"select":  model.OrderShipmentColumns.OrderID,
				}, 2, preFilterMap)
				if err != nil {
					return nil, err
				}

				count = preCount
				if len(preList) > 0 {
					oidMap := make(map[uint64]int) // Map: model.OrderShipmentColumns.OrderID => int
					for i, item := range preList {
						if _, exist := oidMap[item.OrderID]; !exist {
							oids = append(oids, item.OrderID)
							oidMap[item.OrderID] = i
						}
					}

					filterMap[model.OrderColumns.ID] = map[string][]interface{}{
						"in": oids,
					}
					tmpList, _, tmpErr := l.svcCtx.Order.GetList(l.ctx, model.M{
						"search":  in.Search,
						"orderBy": model.OrderColumns.ID,
						"select":  strings.Join(selectCols, ","),
					}, -1, filterMap)
					if tmpErr != nil {
						return nil, tmpErr
					}

					tmpMap := make(map[interface{}]*model.Order)
					for _, tmp := range tmpList {
						tmpMap[tmp.ID] = tmp
					}
					for _, oid := range oids {
						if tmp, exist := tmpMap[oid]; exist {
							orderList = append(orderList, tmp)
						}
					}
				}
			}
		}
	} else {
		orderList, count, err = l.svcCtx.Order.GetList(l.ctx, model.M{
			"offset":  uint32(in.Offset),
			"limit":   uint32(in.Limit),
			"search":  in.Search,
			"orderBy": model.OrderColumns.ID + " DESC",
			"select": strings.Join(selectCols, ","),
		}, 2, filterMap)
		if err != nil {
			return nil, err
		}
	}

	var trades []*order.DetailResponse
	if len(orderList) > 0 {
		var oids []interface{} // []model.Order.ID
		for _, trade := range orderList {
			oids = append(oids, trade.ID)
		}

		subRpcEntitySettingMap := make(map[string]map[string]*model.ColumnSetting)
		if eagerLoadRpcEntities, hasEagerLoadRpcEntities := eagerLoadMap[orderEntityName]; hasEagerLoadRpcEntities {
			for _, entity := range eagerLoadRpcEntities {
				if entity == "total" {
					if preTotal >= 0 {
						total = preTotal
					} else {
						_, total, err = l.svcCtx.Order.GetList(l.ctx, model.M{
							"search": in.Search,
						}, 0, filterMap)
						if err != nil {
							total = 0
						}
					}
					continue
				}

				if rpcEntitySettingMap, hasRpcEntitySettingMap := model.RpcEntityPreloadMap[entity]; hasRpcEntitySettingMap {
					subRpcEntitySettingMap[entity] = rpcEntitySettingMap
				}
			}
		}

		orderItemEntityName := model.RpcEntityNames.OrderItem
		orderItemRepositoryName := model.RepositoryNames.OrderItem
		idItemMap := make(map[uint64][]*order.OrderItem) // Map: model.Order.ID => *order.OrderItem
		if _, hasRpcEntitySettingMap := subRpcEntitySettingMap[orderItemEntityName]; hasRpcEntitySettingMap {
			preloadFields, hasPreloadFields := preloadMap[orderItemEntityName]
			subColumnMap, hasSubColumnMap := model.RepositoryPreloadMap[orderItemRepositoryName]
			if hasPreloadFields && hasSubColumnMap {
				selectColMap := make(map[string]string)
				for _, field := range preloadFields {
					if _, exist := selectColMap[field]; !exist {
						if column, hasColumn := subColumnMap[field]; hasColumn {
							selectColMap[field] = column
						} else {
							selectColMap[field] = ""
						}
					}
				}
				var subSelectCols []string
				for _, column := range selectColMap {
					if column != "" {
						subSelectCols = append(subSelectCols, column)
					}
				}
				orderItemFilterMap := make(map[string]map[string][]interface{})
				orderItemFilterMap[model.OrderItemColumns.OrderID] = map[string][]interface{}{
					"in": oids,
				}
				// orderItemFilterMap[model.OrderItemColumns.Status] = map[string][]interface{}{ // 不查询删除的
				// 	"equalTo": []interface{}{enumx.ObjectStatusAproved},
				// }
				orderItemList, _, err := l.svcCtx.Order.GetItemList(l.ctx, model.M{
					"select": strings.Join(subSelectCols, ","),
				}, -1, orderItemFilterMap)
				if err != nil {
					return nil, err
				}

				if len(orderItemList) > 0 {
					for _, orderItem := range orderItemList {
						idItemMap[orderItem.OrderID] = append(idItemMap[orderItem.OrderID], &order.OrderItem{
							Id:             int64(orderItem.OrderItemID),
							OrderId:        int64(orderItem.OrderID),
							SkuId:          int64(orderItem.SkuID),
							Sku:            orderItem.Sku,
							ImageUrl:       orderItem.SkuImageURL,
							ProductId:      int64(orderItem.ProductID),
							EntryId:        int64(orderItem.EntryID),
							Title:          orderItem.Title,
							Slug:           orderItem.Slug,
							ProductType:    int32(orderItem.ProductType),
							BrandId:        int64(orderItem.BrandID),
							BrandTitle:     orderItem.BrandTitle,
							BrandSlug:      orderItem.BrandSlug,
							GemPrice:       int64(orderItem.GemPrice),
							RetailPrice:    int64(orderItem.RetailPrice),
							Quantity:       int32(orderItem.Quantity),
							Weight:         int64(orderItem.Weight),
							TradeState:     int32(orderItem.TradeState),
							Status:         int32(orderItem.Status),
							GemAmount:      int64(orderItem.GemAmount),
							RetailAmount:   int64(orderItem.RetailAmount),
							Freight:        int64(orderItem.Freight),
							DiscountAmount: int64(orderItem.DiscountAmount),
							AdjustAmount:   int64(orderItem.AdjustAmount),
							ActualAmount:   int64(orderItem.ActualAmount),
							RefundAmount:   int64(orderItem.RefundAmount),
							RefundQuantity: int32(orderItem.RefundQuantity),
						})
					}
				}
			}
		}

		for _, trade := range orderList {
			var items []*order.OrderItem
			if ois, exist := idItemMap[trade.ID]; exist {
				items = ois[:]
			}
			trades = append(trades, &order.DetailResponse{
				Id:             int64(trade.ID),
				ParentId:       int64(trade.ParentID),
				UserId:         int64(trade.UserID),
				Buyer:          trade.Buyer,
				Sn:             trade.OrderSN,
				OrderType:      int32(trade.OrderType),
				SourceType:     int32(trade.SourceType),
				Quantity:       int32(trade.Quantity),
				GemAmount:      int64(trade.GemAmount),
				RetailAmount:   int64(trade.RetailAmount),
				Freight:        int64(trade.Freight),
				DiscountAmount: int64(trade.DiscountAmount),
				AdjustAmount:   int64(trade.AdjustAmount),
				DepositAmount:  int64(trade.DepositAmount),
				BalanceAmount:  int64(trade.BalanceAmount),
				Amount:         int64(trade.ActualAmount),
				RefundAmount:   int64(trade.RefundAmount),
				Gems:           int64(trade.Gems),
				TradeState:     int32(trade.TradeState),
				Remark:         trade.Remark,
				CreatedAt:      modelx.FormatTime(trade.CreatedAt, ""),
				UpdatedAt:      modelx.FormatTime(trade.UpdatedAt, ""),
				PaidAt:         modelx.FormatNullDotTime(trade.PaidAt, ""),
				ShippedAt:      modelx.FormatNullDotTime(trade.ShippedAt, ""),
				DeliveredAt:    modelx.FormatNullDotTime(trade.DeliveredAt, ""),
				ConfirmedAt:    modelx.FormatNullDotTime(trade.ConfirmedAt, ""),
				ReviewedAt:     modelx.FormatNullDotTime(trade.ReviewedAt, ""),
				FulfilledAt:    modelx.FormatNullDotTime(trade.FulfilledAt, ""),
				ClosedAt:       modelx.FormatNullDotTime(trade.ClosedAt, ""),
				Comments:       int32(trade.Comments),
				Growth:         int32(trade.Growth),
				Integrals:      int32(trade.Integrals),
				Items:          items,
			})
		}
	}

	var hasNextPage int32
	var hasPreviousPage int32
	if count > 1 {
		hasNextPage = 1
		count = count - 2
	}
	if count > 0 {
		hasPreviousPage = 1
	}
	return &order.ListResponse{
		Data: trades,
		HasNextPage: hasNextPage,
		HasPreviousPage: hasPreviousPage,
		Total: total,
	}, nil
}
